use async_graphql::ID;
use async_graphql::{Error, ErrorExtensions};
use rbatis::crud::{Skip, CRUD};
use rbatis::rbatis::Rbatis;

use crate::dbs::sqlite::Peer;
use crate::features::explorer::modelsdbo::{
  EditorDBO, FileDBO, FolderDBO, NewFileDBO, NewFolderDBO, UpdateEditorDBO, UpdateFileDBO,
  UpdateFolderDBO,
};
use crate::features::explorer::modelsgqo::{
  CloseEditor, Editor, EditorFilterIn, File, FileFilterIn, Folder, FolderFilterIn, NewFile,
  NewFolder, OpenEditor, UpdateFile, UpdateFolder,
};
use crate::features::explorer::repository::{self};
use crate::features::quizzes::modelsdbo::QuizDBO;
use crate::util::constant::GqlResult;

pub async fn find_top_folder(rbatis_pool: &Rbatis) -> Option<Folder> {
  Some(Folder {
    id: ID::from("fldr:0"),
    folder_name: "Home".to_string(),
    title: Some("Home Folder Default for you, initialized by system hardcoded".to_string()),
    ..Default::default()
  })
}

pub async fn get_sub_folders(rbatis_pool: &Rbatis, pid: &str) -> Option<Vec<FolderDBO>> {
  let db_result = repository::list_sub_folders(rbatis_pool, pid).await;

  if db_result.is_ok() {
    db_result.unwrap()
  } else {
    None
  }
}

pub async fn get_folder_files(rbatis_pool: &Rbatis, folder_id: &str) -> Option<Vec<FileDBO>> {
  let db_result = repository::list_folder_files(rbatis_pool, folder_id).await;

  if db_result.is_ok() {
    db_result.unwrap()
  } else {
    None
  }
}

pub async fn get_folder_quizzes(rbatis_pool: &Rbatis, folder_id: &str) -> Option<Vec<QuizDBO>> {
  let db_result = repository::list_folder_quizzes(rbatis_pool, folder_id).await;

  if db_result.is_ok() {
    db_result.unwrap()
  } else {
    None
  }
}

pub async fn find_folder(rbatis_pool: &Rbatis, folder_id: &str) -> Option<Folder> {
  let folder_dbo = repository::find_folder_by_id(rbatis_pool, folder_id).await;
  if folder_dbo.is_ok() {
    Some(folder_dbo.unwrap().into())
  } else {
    None
  }
}

/// new folder  
pub async fn new_folder(rbatis_pool: &Rbatis, input: NewFolder) -> GqlResult<Folder> {
  let new_folder_dbo = NewFolderDBO {
    folder_name: input.folder_name,
    title: input.title,
    description: input.description,
    referer_url: input.referer_url,
    pid: input.pid.0,
    ..Default::default()
  };

  let db_result = repository::insert_folder(rbatis_pool, &new_folder_dbo).await;

  if db_result.is_ok() {
    Ok(db_result.unwrap().into())
  } else {
    Err(
      Error::new("Insert Folder Error").extend_with(|_, e| e.set("details", "folders table error")),
    )
  }
}

/// update folder  
pub async fn update_folder(
  rbatis_pool: &Rbatis,
  folder_id: &str,
  input: UpdateFolder,
) -> GqlResult<Folder> {
  let in_pid = match input.pid {
    Some(v) => Some(v.0),
    _ => None,
  };

  let update_folder_dbo = UpdateFolderDBO {
    folder_name: input.folder_name,
    title: input.title,
    description: input.description,
    referer_url: input.referer_url,
    pid: in_pid,
    ..Default::default()
  };

  let db_result = repository::update_folder(rbatis_pool, &folder_id, &update_folder_dbo).await;

  if db_result.is_ok() {
    let folder_dbo = repository::find_folder_by_id(rbatis_pool, folder_id).await;
    if folder_dbo.is_ok() {
      Ok(folder_dbo.unwrap().into())
    } else {
      Err(Error::new("Update Folder Error").extend_with(|_, e| e.set("details", "not found by id")))
    }
  } else {
    Err(
      Error::new("Update Folder Error").extend_with(|_, e| e.set("details", "folders table error")),
    )
  }
}

/// 查询文件夹Folder
pub async fn get_folders_by(
  rbatis_pool: &Rbatis,
  my_peer: &Peer,
  filter: Option<FolderFilterIn>,
) -> Result<Vec<FolderDBO>, rbatis::Error> {
  let wrapper = rbatis_pool.new_wrapper().eq("status", "opened");
  let result = rbatis_pool
    .fetch_list_by_wrapper::<FolderDBO>(wrapper.order_by(true, &["updated_at"]).limit(30))
    .await;
  result
}

/// new file  
pub async fn new_file(rbatis_pool: &Rbatis, input: NewFile) -> GqlResult<File> {
  let new_file_dbo = NewFileDBO {
    file_name: input.file_name,
    title: input.title,
    description: input.description,
    referer_url: input.referer_url,
    folder_id: input.folder_id.0,
    ..Default::default()
  };

  let db_result = repository::insert_file(rbatis_pool, &new_file_dbo).await;

  if db_result.is_ok() {
    Ok(db_result.unwrap().into())
  } else {
    Err(Error::new("Insert File Error").extend_with(|_, e| e.set("details", "files table error")))
  }
}

/// update file  
pub async fn update_file(
  rbatis_pool: &Rbatis,
  file_id: &str,
  input: UpdateFile,
) -> GqlResult<File> {
  let in_folder_id = match input.folder_id {
    Some(v) => Some(v.0),
    _ => None,
  };

  let update_file_dbo = UpdateFileDBO {
    file_name: input.file_name,
    title: input.title,
    description: input.description,
    referer_url: input.referer_url,
    content: input.content,
    folder_id: in_folder_id,
    ..Default::default()
  };

  let db_result = repository::update_file(rbatis_pool, &file_id, &update_file_dbo).await;

  if db_result.is_ok() {
    let file_dbo = repository::find_file_by_id(rbatis_pool, file_id).await;
    if file_dbo.is_ok() {
      Ok(file_dbo.unwrap().into())
    } else {
      Err(Error::new("Update File Error").extend_with(|_, e| e.set("details", "not found by id")))
    }
  } else {
    Err(Error::new("Update File Error").extend_with(|_, e| e.set("details", "files table error")))
  }
}

/// open editor  
pub async fn open_editor(rbatis_pool: &Rbatis, input: OpenEditor) -> GqlResult<Editor> {
  let editor_id = input.id.0;
  let update_editor_dbo = UpdateEditorDBO {
    title: Some(input.title.to_owned()),
    status: Some("opened".to_string()),
    ..Default::default()
  };

  let db_result = repository::upsert_editor(rbatis_pool, &editor_id, &update_editor_dbo).await;

  if db_result.is_ok() {
    Ok(db_result.unwrap().into())
  } else {
    Err(Error::new("Open Editor Error").extend_with(|_, e| e.set("details", "editor table error")))
  }
}

/// close editor  
pub async fn close_editor(rbatis_pool: &Rbatis, input: CloseEditor) -> GqlResult<Editor> {
  let editor_id = input.id.0;
  let update_editor_dbo = UpdateEditorDBO {
    status: Some("closed".to_string()),
    ..Default::default()
  };

  let db_result = repository::update_editor(rbatis_pool, &editor_id, &update_editor_dbo).await;

  if db_result.is_ok() {
    let editor_dbo = repository::find_editor_by_id(rbatis_pool, &editor_id).await;
    if editor_dbo.is_ok() {
      Ok(editor_dbo.unwrap().into())
    } else {
      Err(Error::new("Update Editor Error").extend_with(|_, e| e.set("details", "not found by id")))
    }
  } else {
    Err(
      Error::new("Update Editor Error").extend_with(|_, e| e.set("details", "editors table error")),
    )
  }
}

/// 查询文件File
pub async fn get_files_by(
  rbatis_pool: &Rbatis,
  my_peer: &Peer,
  filter: Option<FileFilterIn>,
) -> Result<Vec<FileDBO>, rbatis::Error> {
  let wrapper = rbatis_pool.new_wrapper().eq("status", "opened");
  let result = rbatis_pool
    .fetch_list_by_wrapper::<FileDBO>(wrapper.order_by(true, &["file_name"]).limit(30))
    .await;
  result
}

/// 查询编辑器Editor
pub async fn get_editors_by(
  rbatis_pool: &Rbatis,
  my_peer: &Peer,
  filter: Option<EditorFilterIn>,
) -> Result<Vec<EditorDBO>, rbatis::Error> {
  let wrapper = rbatis_pool.new_wrapper().eq("status", "opened");
  let result = rbatis_pool
    .fetch_list_by_wrapper::<EditorDBO>(wrapper.order_by(true, &["updated_at"]).limit(30))
    .await;
  result
}
